package com.wangzz.mallchat.common.user.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.wangzz.mallchat.common.user.dao.UserDao;
import com.wangzz.mallchat.common.user.domain.entity.User;
import com.wangzz.mallchat.common.user.service.UserService;
import com.wangzz.mallchat.common.user.service.WebSocketService;
import com.wangzz.mallchat.common.user.service.WxMsgService;
import com.wangzz.mallchat.common.user.service.adapter.TextBuilder;
import com.wangzz.mallchat.common.user.service.adapter.UserAdapter;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import java.net.URLEncoder;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

@Service
@Slf4j
public class WxMsgServiceImpl implements WxMsgService {

    @Autowired
    private UserDao userDao;

    @Autowired
    @Lazy
    private WxMpService wxMpService;

    @Autowired
    private UserService userService;

    @Autowired
    private WebSocketService webSocketService;

    @Value("${wx.mp.callback}")
    private String callback;
    public static final String URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect";

    // openid和code的映射关系 todo 考虑内存移除情况
    public static final ConcurrentHashMap<String, Integer> TAMP_OPENID_CODE_MAP = new ConcurrentHashMap<>();

    @Override
    public WxMpXmlOutMessage scan(WxMpXmlMessage wxMpXmlMessage) {

        // 2.获取openid
        String openId = wxMpXmlMessage.getFromUser();
        // 3.解析code ，code来源可能是关注，也可能是关注后，扫码也可能不带code（扫别的码）
        Integer code = getEventKey(wxMpXmlMessage);
        if (Objects.isNull(code)) {
            return null;
        }
        // 4.校验用户是否存在且授权
        User user = userDao.queryUserByOpenId(openId);
        // 5.授权用户直接登录
        boolean registered = Objects.nonNull(user);
        boolean authorized = registered && StrUtil.isNotBlank(user.getAvatar());
        if (registered && authorized) {
            // 登录逻辑，通过code找到给channel推送消息
            webSocketService.handleLoginSuccess(code, user.getId());
        }
        if (!registered) {
            // 用户注册
            User insert = UserAdapter.buildUserSave(openId);
//            userDao.save(insert);
            userService.register(insert);
        }
        // 存储code和openid关系
        TAMP_OPENID_CODE_MAP.put(openId, code);
        // 扫码中处理，推送等待点击登录进行授权
        webSocketService.waitAuthorize(code);
        // 6.没授权生成二维码
        String authUrl = String.format(URL, wxMpService.getWxMpConfigStorage().getAppId(), URLEncoder.encode(callback + "/wx/portal/public/callBack"));

        return new TextBuilder().build("请点击登录：<a href=\"" + authUrl + "\">登录</a>", wxMpXmlMessage);
        // return TextBuilder.build 不返回数据
    }

    /**
     * 用户授权
     *
     * @param userInfo
     */
    @Override
    public void authorize(WxOAuth2UserInfo userInfo) {
        // 查询用户
        User user = userDao.queryUserByOpenId(userInfo.getOpenid());
        if (StrUtil.isEmpty(user.getName())) {
            // 没有授权，填充授权信息
            fillUserInfo(user.getId(), userInfo);
        }
        // 给客户端推送登录消息
        Integer code = TAMP_OPENID_CODE_MAP.get(userInfo.getOpenid());
        webSocketService.handleLoginSuccess(code, user.getId());
    }

    /**
     * 填充授权信息
     *
     * @param uid
     * @param userInfo
     */
    private void fillUserInfo(Long uid, WxOAuth2UserInfo userInfo) {
        // 用户信息封装
        User update = UserAdapter.buildAuthorizeUser(uid, userInfo);
        for (int i = 0; i < 5; i++) {
            try {
                userDao.updateById(update);
                return;
            } catch (DuplicateKeyException e) {
                log.info("fillUserInfo duplicate uid:{},userInfo:{}", uid, userInfo);
            } catch (Exception e) {
                log.error("fill User Info error uid:{},userInfo:{}", uid, userInfo);
            }
            update.setName("用户" + RandomUtil.randomInt(100000));
        }
    }

    /**
     * 扫码事件code处理
     *
     * @param wxMpXmlMessage
     * @return
     */
    private Integer getEventKey(WxMpXmlMessage wxMpXmlMessage) {
        try {
            String code = wxMpXmlMessage.getEventKey();
            code = code.replace("qrscene_", "");
            return Integer.valueOf(code);
        } catch (Exception e) {
            log.info("getEventKey error:{}", wxMpXmlMessage.getEventKey(), e);
            return null;
        }
    }
}
